/*******************************
 * utf-8
 *
 * sphinx http api
 *
 * zhaigy@ucweb.com 2011-04
 *
 * *****************************/
//TODO 搜索功能补全
//TODO 错误异常返回值检查
//TODO 配置文件，记录日志
//TODO 统计、调试功能	<----正在做！！！
//TODO 连接池

#include <cctype>
#include <cstring>

#include <event.h>
#include <evhttp.h>

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <list>
#include <sstream>
#include <algorithm>
#include <functional>

#include "sphinxclient.h"
#include "utils.h"
#include "../json/json/json.h"
#include "../log/log.h"
#include "Config.h"
#include "SphinxQueryData.h"
#include "SearchResult.h"

using namespace std;

/**
 * 记录用户的输入参数
 */
struct GlobalParameter
{
	GlobalParameter() :
		is_daemon(false), host("0.0.0.0"), port(2046),
				proxyed_host("localhost"), proxyed_port(9312)
	{
	}
	bool is_daemon;
	string host;
	int port;
	string proxyed_host;
	int proxyed_port;
};

/**
 * 记录程序运行统计信息
 */
struct Statistic
{
	long long query_count;//查询总次数
	long long query_error_count;//查询出错次数
	long long start_time;//启动时间
};

/**********************全局变量**********************/
//注意：以下两个都是线程级别的，并非真正的全局变量，这里是暂时的
sphinx_client * gp_client;
Json::Writer * gp_json_writer;
Statistic * gp_statistic;
GlobalParameter * gp_parameter;
/**************************************************/

/**
 * 实现从输入到查询数据的初步转换
 */
QueryData parserQuery(evkeyvalq & http_query)
{
	QueryData qd;
	//已经处理全部预定义Key
	for (evkeyval * evkv = http_query.tqh_first, *evkvEnd =
			*http_query.tqh_last; evkv != 0; evkv = evkv->next.tqe_next)
	{
		DEBUG_LOG("evkv->key:%s, evkv->value:%s.",evkv->key,evkv->value);
		if (strcmp("index", evkv->key) == 0)
		{
			qd.index = evkv->value;
		}
		else if (string("q") == evkv->key)
		{
			qd.q = evkv->value;
		}
		else if (string("filter") == evkv->key)
		{
			qd.filters.push_back(evkv->value);
		}
		else if (string("idrange") == evkv->key)
		{
			qd.idrange = evkv->value;
		}
		else if (string("start") == evkv->key)
		{
			qd.start = atoi(evkv->value);
		}
		else if (string("rows") == evkv->key)
		{
			qd.rows = atoi(evkv->value);
		}
		else if (string("fieldweights") == evkv->key)
		{
			qd.fieldweights = evkv->value;
		}
		else if (string("retries") == evkv->key)
		{
			qd.retries = atoi(evkv->value);
		}
		else if (string("maxquerytime") == evkv->key)
		{
			qd.maxquerytime = atoi(evkv->value);
		}
		else if (string("connecttimeout") == evkv->key)
		{
			qd.connecttimeout = atoi(evkv->value);
		}
		else if (string("groupby") == evkv->key)
		{
			qd.groupby = evkv->value;
		}
		else if (string("groupdistinct") == evkv->key)
		{
			qd.groupdistinct = evkv->value;
		}
		else if (string("matchmode") == evkv->key)
		{
			qd.matchmode = evkv->value;
		}
		else if (string("rankingmode") == evkv->key)
		{
			qd.rankingmode = evkv->value;
		}
		else if (string("sortmode") == evkv->key)
		{
			qd.sortmode = evkv->value;
		}
		else if (string("select") == evkv->key)
		{
			qd.select = evkv->value;
		}
		else
		{
			WARN_LOG("不支持的输入值,key:%s, value:%s",evkv->key,evkv->value);
		}

		if (evkv == evkvEnd)
		{
			break;
		}
	}
	return qd;
}

/**
 * 调用客户端接口查询
 * TODO 重点！！！
 */
void search(const SphinxQueryData & sqd, SearchResult * sr)
{
	DEBUG_LOG("search...");
	sphinx_client * const client = gp_client;
	sphinx_reset_filters(client);
	sphinx_reset_groupby(client);

	sr->set_client(client);
	//查询之前的准备是否OK
	//bool is_ok=true;

	//filter - 属性值，属性范围，属性范围float,id范围（仅可以设置一个值）
	if (!sqd.filters.empty())
	{
		for (list<SphinxQueryData::Filter>::const_iterator it =
				sqd.filters.begin(); it != sqd.filters.end(); ++it)
		{
			const SphinxQueryData::Filter & filter = *it;
			if (filter.type == SphinxQueryData::Filter::kValue)
			{
				if (sphinx_add_filter(client, filter.attr_name.c_str(),
						filter.values_num, filter.values, filter.exclude)
						!= SPH_TRUE)
				{
					return;
				}
			}
			else if (filter.type == SphinxQueryData::Filter::kIntRange)
			{//
				if (sphinx_add_filter_range(client, filter.attr_name.c_str(),
						filter.int_min, filter.int_max, filter.exclude)
						!= SPH_TRUE)
				{
					return;
				}
			}
			else if (filter.type == SphinxQueryData::Filter::kFloatRange)
			{
				if (sphinx_add_filter_range(client, filter.attr_name.c_str(),
						filter.float_min, filter.float_max, filter.exclude)
						!= SPH_TRUE)
				{
					return;
				}
			}
			else
			{
				throw "Filter 未知的设置类型";
			}
		}
	}
	if (sqd.idrange_min > 0L)
	{//id=0 这个在sphinx中是不合法的
		if (sphinx_set_id_range(client, sqd.idrange_min, sqd.idrange_max)
				!= SPH_TRUE)
		{
			return;
		}
	}

	//第4个参数是否可以设置为rows*10000?
	if (sphinx_set_limits(client, sqd.start, sqd.rows, sqd.start + sqd.rows, 0)
			!= SPH_TRUE)
	{
		return;
	}

	//fieldweight - 字段数，字段名数组，权重数组（整数）
	if (sqd.fieldweights_num > 0)
	{
		if (sphinx_set_field_weights(client, sqd.fieldweights_num,
				sqd.fieldweights_fields, sqd.fieldweights_weights) != SPH_TRUE)
		{
			return;
		}
	}

	if (sqd.retries > 0)
	{
		if (sphinx_set_retries(client, sqd.retries, 100) != SPH_TRUE)
		{
			//间隔毫秒
			return;
		}
	}
	if (sqd.maxquerytime > 0)
	{
		if (sphinx_set_max_query_time(client, sqd.maxquerytime) != SPH_TRUE)
		{
			//毫秒
			return;
		}
	}
	if (sqd.connecttimeout > 0)
	{
		if (sphinx_set_connect_timeout(client, float(sqd.connecttimeout) / 1000)
				!= SPH_TRUE)
		{
			//转换为秒
			return;
		}
	}

	//groupby - groupfun , sort字符串
	if (!sqd.groupby_attr.empty())
	{
		if (sphinx_set_groupby(client, sqd.groupby_attr.c_str(),
				sqd.groupby_func, sqd.groupby_sort.c_str()) != SPH_TRUE)
		{
			return;
		}
		if (!sqd.groupdistinct.empty())
		{
			if (sphinx_set_groupby_distinct(client, sqd.groupdistinct.c_str())
					!= SPH_TRUE)
			{
				return;
			}
		}
	}

	if (sphinx_set_match_mode(client, sqd.matchmode) != SPH_TRUE
			|| sphinx_set_ranking_mode(client, sqd.rankingmode) != SPH_TRUE
			|| sphinx_set_sort_mode(client, sqd.sortmode,
					sqd.sortclause.c_str()) != SPH_TRUE)
	{
		return;
	}

	if (!sqd.select.empty())
	{
		if (sphinx_set_select(client, sqd.select.c_str()) != SPH_TRUE)
		{
			return;
		}
	}
	//sphinx_set_connect_timeout(client,1);
	sphinx_result * res = sphinx_query(client, sqd.q.c_str(),
			sqd.index.c_str(), NULL );
	sr->set_result(res);
}

/**使用jsoncpp实现的转换*/
string convert_to_json_string(const SearchResult & sr)
{
	Json::Value root;
	//拼输出------------
	root["status_info"]
			= "0=SEARCHD_OK, 1=SEARCHD_ERROR; 2=SEARCHD_RETRY; 3=SEARCHD_WARNING";
	root["status"] = sr.getStatus();
	if (sr.getStatus() == SEARCHD_ERROR)
	{
		root["error"] = sr.getError();
	}
	else if (sr.getStatus() == SEARCHD_RETRY)
	{
		//无任何说明
	}
	else
	{//ok warning
		if (sr.getStatus() == SEARCHD_WARNING)
		{
			root["warning"] = sr.getWarning();
		}
		root["total"] = sr.getTotal();
		root["time"] = sr.getTime();
		int matchNumb = sr.getMatchNumb();
		root["matchNumb"] = matchNumb;
		//匹配集合 root["matches"];
		for (int i = 0; i < matchNumb; ++i)
		{
			Json::Value match;
			match["id"] = (long long) sr.getId(i);
			int attrNumb = sr.getAttrNumb();
			for (int j = 0; j < attrNumb; ++j)
			{
				if (sr.getAttrType(j) == (SPH_ATTR_MULTI | SPH_ATTR_INTEGER))
				{
					unsigned int * mva = sr.getAttrMultiIntValue(i, j);
					Json::Value array_temp;
					for (unsigned int k = 0; k < mva[0]; ++k)
					{
						array_temp.append(mva[1 + k]);
					}
					match[sr.getAttrName(j)] = array_temp;
				}
				else if (sr.getAttrType(j) == SPH_ATTR_FLOAT)
				{
					match[sr.getAttrName(j)] = sr.getAttrFloatValue(i, j);
				}
				else
				{//int
					match[sr.getAttrName(j)] = sr.getAttrIntValue(i, j);
				}
			}
			//这里添加进来
			root["matches"].append(match);
		}

		//for words
		int wordNumb = sr.getWordNumb();
		root["wordNumb"] = wordNumb;
		//words
		for (int i = 0; i < wordNumb; ++i)
		{
			Json::Value word;
			word["word"] = sr.getWord(i);
			word["hits"] = sr.getHits(i);
			word["docs"] = sr.getDocs(i);
			root["words"].append(word);
		}
	}

	return gp_json_writer->write(root);
}

string make_inner_error_json_string(string e)
{
	Json::Value root;
	root["status_info"]
			= "0=SEARCHD_OK, 1=SEARCHD_ERROR; 2=SEARCHD_RETRY; 3=SEARCHD_WARNING";
	root["status"] = SEARCHD_ERROR;
	root["error"] = e;
	return gp_json_writer->write(root);
}

/**
 * 返回结果必须是json格式，不能抛出异常
 */
string deal_search_handler(struct evkeyvalq http_query)
{
	try
	{
		QueryData queryData = parserQuery(http_query);
		if (queryData.q.empty())
		{
			throw "查询语句异常";
		}
		SphinxQueryData sqd(queryData);
		SearchResult searchResult;
		search(sqd, &searchResult);
		//即便是查询失败，也应该到此，但如发生异常，不会到此处
		return convert_to_json_string(searchResult);
	} catch (const char * e)
	{
		ERROR_LOG("异常:%s",e);
		return make_inner_error_json_string(string("异常：") + e);
	} catch (const string & e)
	{
		ERROR_LOG("异常:%s",e.c_str());
		return make_inner_error_json_string("异常：" + e);
	} catch (...)
	{
		ERROR_LOG("未知的异常");
		return make_inner_error_json_string("未知的异常");
	}
}

/**
 * 处理状态请求
 */
string deal_status_handler(void)
{
	Json::Value root;
	root["status_info"]
			= "0=SEARCHD_OK, 1=SEARCHD_ERROR; 2=SEARCHD_RETRY; 3=SEARCHD_WARNING";

	sphinx_client * const client = gp_client;
	int row_num;
	int col_num;
	char ** status = sphinx_status(client, &row_num, &col_num);
	if (!status)
	{
		root["status"] = SEARCHD_ERROR;
		root["error"] = sphinx_error(client);
	}
	else
	{
		root["status"] = SEARCHD_OK;
		for (int i = row_num * col_num - 1; i >= 0; i -= 2)
		{
			root[status[i - 1]] = status[i];
		}
	}
	string json = gp_json_writer->write(root);
	sphinx_status_destroy(status, row_num, col_num);
	return json;
}

/**处理器,注意,此函数内部如无系统崩溃，不会报错，所有异常应控制在本函数内部*/
void http_handler(struct evhttp_request *req, void *arg)
{
	try
	{
		gp_statistic->query_count++;
		struct evbuffer *buf;
		buf = evbuffer_new();
		if (buf == NULL)
		{
			gp_statistic->query_error_count++;
			//throw "failed to create response buffer";
			WARN_LOG("failed to create response buffer");
			return;
		}
		try
		{
			const evhttp_uri * uri = evhttp_request_get_evhttp_uri(req);
			string decode_uri(evhttp_request_uri(req));
			DEBUG_LOG("decode_uri:%s",decode_uri.c_str());

			string path = evhttp_uri_get_path(uri);
			DEBUG_LOG("path:%s",path.c_str());

			// 返回给浏览器的信息
			evhttp_add_header(req->output_headers, "Content-Type",
					"text/plain; charset=UTF-8");
			evhttp_add_header(req->output_headers, "Server", "my_httpd");
			evhttp_add_header(req->output_headers, "Connection", "keep-alive");
			//evhttp_add_header(req->output_headers, "Connection", "close");

			if (path == "/search")
			{//处理检索
				struct evkeyvalq http_query;
				evhttp_parse_query(decode_uri.c_str(), &http_query);
				string json = deal_search_handler(http_query);
				DEBUG_LOG("json:%s",json.c_str());
				evbuffer_add_printf(buf, json.c_str(), NULL);
				evhttp_clear_headers(&http_query);
			}
			else if (path == "/status")
			{
				DEBUG_LOG("/status");
				//本服务的统计信息 -- 可省略？
				//代理的统计信息
				string json = deal_status_handler();
				evbuffer_add_printf(buf, json.c_str(), NULL);
			}
			else
			{
				WARN_LOG("未知的路径");
			}
			INFO_LOG("[%s] OK",path.c_str());
		} catch (string err)
		{
			ERROR_LOG(err.c_str());
			gp_statistic->query_error_count++;
		} catch (...)
		{
			ERROR_LOG("未知错误");
			gp_statistic->query_error_count++;
		}
		evhttp_send_reply(req, HTTP_OK, "OK", buf);
		// 内存释放
		evbuffer_free(buf);

		return;
	} catch (string err)
	{
		ERROR_LOG(err.c_str());

	} catch (...)
	{
		ERROR_LOG("未知错误");
	}
	gp_statistic->query_error_count++;
}

/**
 * 加载配置文件
 */
bool load_config_file()
{
	try
	{
		const char ConfigFile[] = "./etc/proxy.ini";
		Config configSettings(ConfigFile);

		gp_parameter->is_daemon = configSettings.Read("is_daemon",
				gp_parameter->is_daemon);
		gp_parameter->host = configSettings.Read("host", gp_parameter->host);
		gp_parameter->port = configSettings.Read("port", gp_parameter->port);
		gp_parameter->proxyed_host = configSettings.Read("proxyed_host",
				gp_parameter->proxyed_host);
		gp_parameter->proxyed_port = configSettings.Read("proxyed_port",
				gp_parameter->proxyed_port);
		return true;
	} catch (Config::File_not_found fnf)
	{
		//文件没有找到
		ERROR_LOG("配置文件没有找到");
	} catch (...)
	{
		ERROR_LOG("未知错误");
	}
	return false;
}

/**
 * 检查参数配置，失败就退出程序
 */
void check_config_parameter()
{
	cout << "is_daemon: " << gp_parameter->is_daemon << endl;
	cout << "host: " << gp_parameter->host << ":" << gp_parameter->port << endl;
	cout << "proxyed_host: " << gp_parameter->proxyed_host << ":"
			<< gp_parameter->proxyed_port << endl;

	if (gp_parameter->host.empty() || gp_parameter->proxyed_host.empty())
	{
		ERROR_LOG("配置不正确");
		exit(1);
	}
}

/**代码移植，非安全退出，待改进*/
void ExecSafeExit(int aiSignal)
{
	struct timeval tv;
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	event_loopexit(&tv);
}

void SetSignalHandle(void)
{
	signal(SIGALRM, SIG_IGN);
	signal(SIGPIPE, SIG_IGN);
	signal(SIGUSR2, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGUSR1, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGTERM, SIG_IGN);

	signal(SIGINT, ExecSafeExit);
	signal(SIGTERM, ExecSafeExit);
	signal(SIGQUIT, ExecSafeExit);
}

int main(int argc, char **argv)
{
	//全局的日志
	g_pLog = new CLog("./logs/main");
	g_pLog->set_log_level(CLog::LOG_EVENT);
	INFO_LOG("开始");
	gp_parameter = new GlobalParameter;
	//配置文件解析
	if (!load_config_file())
	{
		ERROR_LOG("加载配置文件失败");
		return 1;
	}
	//全部参数从配置文件读取,不再接受命令行参数
	check_config_parameter();

	if (gp_parameter->is_daemon)
	{
		if (fork() == 0 /* CHILD PROCESS */)
		{
			INFO_LOG("Daemon mode start.");
			cout << "Daemon mode start." << endl;
		}
		else
		{ //parent
			return 0;
		}
	}

	SetSignalHandle();

	gp_statistic = new Statistic;
	gp_statistic->start_time = time(NULL);

	struct evhttp *httpd;
	event_init();
	httpd = evhttp_start(gp_parameter->host.c_str(), gp_parameter->port);
	if (httpd == NULL)
	{
		INFO_LOG("Unable to listen on %s:%d",gp_parameter->host.c_str(),gp_parameter->port);
		exit(1);
	}

	// 设置请求超时时间
	evhttp_set_timeout(httpd, 3);

	gp_client = sphinx_create(SPH_TRUE);
	sphinx_set_server(gp_client, gp_parameter->proxyed_host.c_str(),
			gp_parameter->proxyed_port);
	sphinx_set_connect_timeout(gp_client, 3.0);
	sphinx_open(gp_client);
	gp_json_writer = new Json::StyledWriter;
	//gp_json_writer=new Json::FastWriter;

	cout << "start, waiting for search..." << endl;
	/* Set a callback for requests to "/specific". */
	/* evhttp_set_cb(httpd, "/specific", another_handler, NULL); */
	/* Set a callback for all other requests. */
	evhttp_set_gencb(httpd, http_handler, NULL);
	event_dispatch();
	/* Not reached in this code as it is now. */
	evhttp_free(httpd);

	sphinx_close(gp_client);
	cout << "main return!!!" << endl;
	INFO_LOG("结束");

	return 0;
}

